//
//  MFWDownloadTask.m
//  download
//
//  Created by 陈曦 on 13-4-24.
//  Copyright (c) 2013年 陈曦. All rights reserved.

#ifndef MFWSdkHeaders
#import "ASIHTTPRequest.h"
#import "ASIDataDecompressor.h"
#endif

#import "MFWDownloadTask.h"
#import "MFWDownloadTaskRequest.h"
#import "FMDatabase.h"
#include "sys/stat.h"
#import "MFWDownloadHeadChecker.h"
#import "MFWDownloadHttpRequest.h"

// 未下载区域结构体
typedef struct {
    unsigned long long begin;
    unsigned long long length;
}MFWDownloadRangeStruct;

@interface NSValue(MFWDownloadRangeStruct)
@property (nonatomic, readonly) MFWDownloadRangeStruct mfwRange;
+(id)valueWithMFWRange:(MFWDownloadRangeStruct)aRange;
@end

@implementation NSValue(MFWDownloadRangeStruct)

- (MFWDownloadRangeStruct)mfwRange
{
    MFWDownloadRangeStruct range;
    [self getValue:&range];
    return range;
}

+ (id)valueWithMFWRange:(MFWDownloadRangeStruct)aRange
{
    return [NSValue valueWithBytes:&aRange objCType:@encode(MFWDownloadRangeStruct)];
}

@end

@interface MFWDownloadTask()<MFWDownloadTaskRequestDelegate>

// 连接
@property (nonatomic, retain) ASIHTTPRequest *headRequest;
@property (atomic,  retain) NSMutableArray *requests;
@property (nonatomic, assign) NSInteger maxThreadNumber;

// 线程锁
@property (nonatomic, retain) NSCondition *writeFileLock;
@property (nonatomic, retain) NSCondition *taskRequestLock;

// 接收http头
@property (nonatomic, readonly) void(^reciveHead)(NSDictionary *aDictionary);

// 基础信息
@property (nonatomic, copy)   NSString           *lastUpdateString;
@property (nonatomic, copy)   NSString           *eTag;
@property (nonatomic, assign) unsigned long long fileLength;
@property (nonatomic, copy)   NSString           *configFile;
@property (nonatomic, retain) NSFileHandle       *fileHandle;
@property (nonatomic, assign) NSUInteger         retryCount;

@property (atomic   , retain) NSMutableArray     *undownloadingRanges;

@end

#define BUFF_SIZE (1024*300)

#define MAX_THREAD_NUMBER 5

@implementation MFWDownloadTask

- (unsigned long long)totalLength
{
    return self.fileLength;
}

- (unsigned long long)downloadLength
{
    if (self.fileLength > 0)
    {
        __block unsigned long long unDownload = 0;
        [self.requests enumerateObjectsUsingBlock:^(MFWDownloadTaskRequest *request, NSUInteger idx, BOOL *stop) {
            unDownload += request.unDownloadSize;
        }];
        
        [self.undownloadingRanges enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger idx, BOOL *stop) {
            MFWDownloadRangeStruct range = value.mfwRange;
            unDownload += range.length;
        }];
        
        return self.fileLength - unDownload;
    }
    return 0;
}

#pragma mark -
#pragma mark 配置文件相关
#define LASTUPDATE_KEY @"lastUpdate"
#define ETAG_KEY @"etag"
#define FILE_LEN_KEY @"fileLength"
#define UNDOWNLOAD_RANGES @"ranges"

- (void)removeConfig
{
    [[NSFileManager defaultManager] removeItemAtPath:self.configFile error:nil];
}

- (void)saveConfigToFile
{
    NSMutableArray *arr = [[NSMutableArray alloc] init];
    // 记录所有线程的剩余进度
    for (NSUInteger i = 0; i < self.requests.count ;i++)
    {
        MFWDownloadTaskRequest *request = [self.requests objectAtIndex:i];
        // 如果该线程下载完 则不记录 理论应该都成立
        if (request.length > 0)
        {
            NSValue *beginValue = [NSNumber numberWithUnsignedLongLong:request.beginOffset];
            NSValue *lengthValue = [NSNumber numberWithUnsignedLongLong:request.length];
            [arr addObject:beginValue];
            [arr addObject:lengthValue];
        }
    }
    [self.undownloadingRanges enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger idx, BOOL *stop) {
        MFWDownloadRangeStruct range = value.mfwRange;
        NSValue *beginValue = [NSNumber numberWithUnsignedLongLong:range.begin];
        NSValue *lengthValue = [NSNumber numberWithUnsignedLongLong:range.length];
        [arr addObject:beginValue];
        [arr addObject:lengthValue];
    }];
    
    // 如果有进度 则保存
    if (arr.count > 0)
    {
        NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
        [dict setValue:self.lastUpdateString forKey:LASTUPDATE_KEY];
        [dict setValue:self.eTag forKey:ETAG_KEY];
        [dict setValue:[NSNumber numberWithUnsignedLongLong:self.fileLength] forKey:FILE_LEN_KEY];
        [dict setValue:arr forKey:UNDOWNLOAD_RANGES];
        [dict writeToFile:self.configFile atomically:YES];
    }
}

- (void)loadConfigFromFile
{
    if (!self.configFile
        || ![[NSFileManager defaultManager] fileExistsAtPath:self.configFile])
    {
        self.lastUpdateString = nil;
        self.eTag = nil;
        self.undownloadingRanges = nil;
        self.fileLength = 0;
        return;
    }
    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:self.configFile];
    self.lastUpdateString = [dict valueForKey:LASTUPDATE_KEY];
    self.eTag = [dict valueForKey:ETAG_KEY];
    self.fileLength = ((NSNumber*)[dict valueForKey:FILE_LEN_KEY]).unsignedLongLongValue;
    
    NSArray *arr = [dict valueForKey:UNDOWNLOAD_RANGES];
    self.undownloadingRanges = [[NSMutableArray alloc] init];
    for (NSInteger i = 0 ;i < arr.count ; i+=2)
    {
        NSNumber *begin = [arr objectAtIndex:i];
        NSNumber *length = [arr objectAtIndex:i+1];
        MFWDownloadRangeStruct range;
        range.begin = begin.unsignedLongLongValue;
        range.length = length.unsignedLongLongValue;
        NSValue *value = [NSValue valueWithMFWRange:range];
        [self.undownloadingRanges addObject:value];
    }
}

#pragma mark -
#pragma mark 初始化相关

- (id)initWithItem:(MFWDownloadItem *)aItem
{
    self = [super init];
    if (self)
    {
        self.item = aItem;
        self.configFile = [aItem.tempPath stringByAppendingPathExtension:@"plist"];
        self.maxThreadNumber = 3;
        self.writeFileLock = [[NSCondition alloc] init];
        self.taskRequestLock = [[NSCondition alloc] init];
        [self loadConfigFromFile];
    }
    return self;
}

- (void)stop
{
    if (!self.fileLength)
    {
        [self clear];
    }
    else
    {
    
        if (!self.undownloadingRanges)
        {
            self.undownloadingRanges = [[NSMutableArray alloc] init];
        }
        for (MFWDownloadTaskRequest *request in self.requests)
        {
            // 先停止 停止会flush和更新range
            [request stop];
            
            // 存储range
            MFWDownloadRangeStruct range;
            range.begin = request.beginOffset;
            range.length = request.length;
            NSValue *value = [NSValue valueWithMFWRange:range];
            [self.undownloadingRanges addObject:value];
        }
        if (self.fileHandle)
        {
            [self.fileHandle closeFile];
            self.fileHandle = nil;
        }
        self.requests = nil;
        
        if (self.headRequest)
        {
            [self.headRequest clearDelegatesAndCancel];
            self.headRequest = nil;
        }
    }
}

- (void)start
{
    [self start:self.item.url];
}

- (void)start:(NSString*)aUrl
{
    if (self.headRequest)
    {
        return;
    }
    
    if(self.requests && self.requests.count > 0)
    {
        return;
    }
    
    MFWDownloadHttpRequest *request = [[MFWDownloadHttpRequest alloc] initWithURL:[NSURL URLWithString:aUrl]];
    request.shouldContinueWhenAppEntersBackground = YES;
    request.shouldCompressRequestBody = NO;
    request.allowCompressedResponse = NO;
    request.timeOutSeconds = 40;
    
    NSString *range = [NSString stringWithFormat:@"bytes=%lld - %lld",1ull,1ull];
    [request addRequestHeader:@"Range" value:range];
    
    self.headRequest = request;
    request.delegate = self;

    [request startAsynchronous];
}


- (void)request:(MFWDownloadHttpRequest *)request didReceiveResponseHeaders:(NSDictionary *)headDic
{
    MFWDownloadHeadChecker *checker = [MFWDownloadHeadChecker checkWithHeadDict:headDic];
    [request clearDelegatesAndCancel];
    if (checker.redirectUrl.length)
    {
        [self stop];
        [self start:checker.redirectUrl];
        return;
    }
    
    // 先撤销head request 我们不需要headrequest的body数据

    
    
    // 读取记录的信息 如果没有  返回空
    // 确认是否可以多线程
    BOOL removeOld = NO;
    if (checker.fileLength > 0
        && checker.beginOffset == 1
        && checker.endOffset == 1/*这里都等于1  上面start函数设置申请的大小*/)
    {
        // do nothing
    }
    // 单线程去下载
    else
    {
        self.maxThreadNumber = 1;
        NSLog(@"max thread = 1");
        removeOld = YES;
    }
    
    // 如果已经存在文件 并且匹配etag和lastupdate 打开
    NSFileManager *manager = [NSFileManager defaultManager];
    if ([checker checkNewestByETag:self.eTag
                    lastUpdateTime:self.lastUpdateString]
        && [manager fileExistsAtPath:self.item.tempPath]
        && [manager fileExistsAtPath:self.configFile])
    {
        self.fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:self.item.tempPath];
    }
    // 否则删掉全部文件
    else
    {
        removeOld = YES;
    }
    
    // 如果需要清除原来的数据 
    if (removeOld)
    {
        if (self.fileHandle)
        {
            [self.fileHandle closeFile];
            self.fileHandle = nil;
        }
        NSFileManager *manager = [NSFileManager defaultManager];
        [manager removeItemAtPath:self.item.tempPath error:nil];
        [manager removeItemAtPath:self.configFile error:nil];
        self.undownloadingRanges = nil;
    }
    
    
    self.eTag = checker.eTag;
    self.lastUpdateString = checker.lastUpdateTime;
    self.fileLength = checker.fileLength;
    
    // 如果前面没有找到文件 或者被清除 这里创建文件
    if (!self.fileHandle)
    {
        [[NSFileManager defaultManager] createFileAtPath:self.item.tempPath
                                                contents:nil
                                              attributes:nil] ;
        NSFileHandle *handle = [NSFileHandle fileHandleForUpdatingAtPath:self.item.tempPath];
        self.fileHandle = handle;
    }
    
    // 开始创建连接
    self.requests = [[NSMutableArray alloc] init];
    
    // 如果是续传 
    if (self.undownloadingRanges.count)
    {
        for (NSInteger i = 0 ;i < self.undownloadingRanges.count ;i++)
        {
            NSValue *value = [self.undownloadingRanges objectAtIndex:i];
            MFWDownloadRangeStruct range = value.mfwRange;
            MFWDownloadTaskRequest *request = [[MFWDownloadTaskRequest alloc] initWithUrl:self.item.url
                                                                                 delegate:self];
            request.beginOffset = range.begin;
            request.length = range.length;
            [request start];
            [self.requests addObject:request];
        }
        self.undownloadingRanges = nil;
    }
    else
    {
        unsigned long long offset = 0;
        unsigned long long size = checker.fileLength / self.maxThreadNumber;
        unsigned long long outSize = self.fileLength;
        
        for (NSInteger i = 0 ; i < self.maxThreadNumber ; i++)
        {
            MFWDownloadTaskRequest *request = [[MFWDownloadTaskRequest alloc] initWithUrl:self.item.url
                                                                                 delegate:self];
            request.beginOffset = offset;
            if (self.maxThreadNumber - 1 == i)
            {
                request.length = outSize;
            }
            else
            {
                request.length = size;
            }
            request.bufferSize = BUFF_SIZE; // 300k
            
            [request start];
            offset += size;
            outSize -= size;
            
            [self.requests addObject:request];
        }
    }
}
// head fail
- (void)requestFailed:(ASIHTTPRequest *)request
{
    if (self.retryCount < 3)
    {
        self.retryCount++;
        [self stop];
        [self start];
    }
    else
    {
//        NSString *url = [NSString stringWithFormat:@"itemurl:%@ requesturl:%@",
//                         self.item.url,
//                         request.url.absoluteString];
        
//        [TGMClickHelper eventDownloadTaskError:request.error.description
//                                           url:url
//                                   requestHead:[MFWJsonUtils stringEncodingJsonObject:request.requestHeaders]
//                                  responseHead:[MFWJsonUtils stringEncodingJsonObject:request.responseHeaders]];
        [self taskRequestFailed:nil];
    }
}

#pragma mark -
#pragma mark delegate

- (void)taskRequest:(MFWDownloadTaskRequest*)aRequest
        getData:(NSData*)aData
       atOffset:(unsigned long long) aOffset
{
    [self.writeFileLock lock];
    
    // 写入块
    [self.fileHandle seekToFileOffset:aOffset];
    [self.fileHandle writeData:aData];
    
    // 更新开始offset和len
    aRequest.beginOffset += aData.length;
    if (aRequest.length > aData.length)
    {
        aRequest.length -= aData.length;
    }
    else
    {
        aRequest.length = 0;
    }
    
    for (MFWDownloadTaskRequest *request in self.requests)
    {
        if (!request.isRunning)
        {
            [request start];
        }
    }
    
    [self.writeFileLock unlock];
    [self saveConfigToFile];
}

- (long long) fileSizeAtPath:(NSString*) filePath{
    struct stat st;
    if(lstat([filePath cStringUsingEncoding:NSUTF8StringEncoding], &st) == 0){
        return st.st_size;
    }
    return 0;
}

- (void)taskRequestFinished:(MFWDownloadTaskRequest*)aRequest
{
    [self.taskRequestLock lock];
    [self.requests removeObject:aRequest];
    
    // 新起线程   首先检查未下载range
    if (self.fileLength > 0)
    {
        MFWDownloadRangeStruct range;
        range.begin = 0;
        range.length = 0;
        if (self.undownloadingRanges.count)
        {
            NSValue *value = [self.undownloadingRanges objectAtIndex:0];
            [self.undownloadingRanges removeObject:value];
            range = value.mfwRange;
        }
        else
        {
            for (MFWDownloadTaskRequest *request in self.requests)
            {
                CGFloat unDownloadSize = request.unDownloadSize;
                if (unDownloadSize > BUFF_SIZE * 4)
                {
                    unsigned long long theNewSize = unDownloadSize / 2;
                    request.length -= theNewSize;
                    range.begin = request.beginOffset + request.length;
                    range.length = theNewSize;
                    break;
                }
            }
        }
        // 如果可以新起线程则开始新线程
        if (range.length > 0)
        {
            MFWDownloadTaskRequest *newRequest = [[MFWDownloadTaskRequest alloc] initWithUrl:self.item.url
                                                                                    delegate:self];
            newRequest.beginOffset = range.begin;
            newRequest.length = range.length;
            newRequest.bufferSize = BUFF_SIZE;
            [newRequest start];
            [self.requests addObject:newRequest];
        }
    }
    
       
    // 如果所有线程都结束 进行结束处理
    if (self.requests.count == 0)
    {
        [self removeConfig];
        if (0 == [self fileSizeAtPath:self.item.tempPath])
        {
            
        }
        [self.fileHandle closeFile];
        if([self.headRequest isResponseCompressed])
        {
            [ASIDataDecompressor uncompressDataFromFile:self.item.tempPath
                                                 toFile:self.item.localPath
                                                  error:nil];
            [[NSFileManager defaultManager] removeItemAtPath:self.item.tempPath error:nil];
        }
        else
        {
            [[NSFileManager defaultManager] removeItemAtPath:self.item.localPath error:nil];
            [[NSFileManager defaultManager] moveItemAtPath:self.item.tempPath
                                                    toPath:self.item.localPath
                                                     error:nil];
        }
        [self.delegate MFWDownloadTaskFinished:self];
    }
    [self.taskRequestLock unlock];
}

- (void)taskRequestFailed:(MFWDownloadTaskRequest*)aRequest
{
    // 如果不能续传 清空
    if(self.fileLength == 0)
    {
        [self clear];
        [self.delegate MFWDownloadTaskFailed:self];
    }
    else
    {
        [self saveConfigToFile];
        if (self.requests.count == 1 && aRequest.retryCount < 3)
        {
            [aRequest start];
        }
        else
        {
            
            BOOL allStop = YES;
            for (MFWDownloadTaskRequest *request in self.requests)
            {
                if (request.isRunning)
                {
                    allStop = NO;
                    break;
                }
            }
            
            if (allStop)
            {
                [self stop];
                [self.delegate MFWDownloadTaskFailed:self];
            }
        }
    }
}

- (void)clear
{
    // 清理连接
    for (MFWDownloadTaskRequest *request in self.requests)
    {
        [request clear];
    }
    self.requests = nil;
    
    self.undownloadingRanges = nil;
    
    [self.headRequest clearDelegatesAndCancel];
    self.headRequest = nil;
    
    // 清理文件
    if (self.fileHandle)
    {
        [self.fileHandle closeFile];
        self.fileHandle = nil;
    }
    
    self.lastUpdateString = nil;
    self.eTag = nil;
    self.fileLength = 0;
    
    NSFileManager *manager = [NSFileManager defaultManager];
    if ([manager fileExistsAtPath:self.item.tempPath])
    {
        [manager removeItemAtPath:self.item.tempPath error:nil];
    }
    if ([manager fileExistsAtPath:self.configFile])
    {
        [manager removeItemAtPath:self.configFile error:nil];
    }
}

@end
